/*******************************************************************************
 * Copyright (c) 2013, Daniel Murphy
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 	* Redistributions of source code must retain the above copyright notice,
 * 	  this list of conditions and the following disclaimer.
 * 	* Redistributions in binary form must reproduce the above copyright notice,
 * 	  this list of conditions and the following disclaimer in the documentation
 * 	  and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

package org.jbox2d.common;

import java.io.Serializable;

/** A 2D column vector */
public class Vec2 implements Serializable {
	private static final long serialVersionUID = 1L;

	public float x, y;

	public Vec2 () {
		this(0, 0);
	}

	public Vec2 (float x, float y) {
		this.x = x;
		this.y = y;
	}

	public Vec2 (Vec2 toCopy) {
		this(toCopy.x, toCopy.y);
	}

	/** Zero out this vector. */
	public final void setZero () {
		x = 0.0f;
		y = 0.0f;
	}

	/** Set the vector component-wise. */
	public final Vec2 set (float x, float y) {
		this.x = x;
		this.y = y;
		return this;
	}

	/** Set this vector to another vector. */
	public final Vec2 set (Vec2 v) {
		this.x = v.x;
		this.y = v.y;
		return this;
	}

	/** Return the sum of this vector and another; does not alter either one. */
	public final Vec2 add (Vec2 v) {
		return new Vec2(x + v.x, y + v.y);
	}

	/** Return the difference of this vector and another; does not alter either one. */
	public final Vec2 sub (Vec2 v) {
		return new Vec2(x - v.x, y - v.y);
	}

	/** Return this vector multiplied by a scalar; does not alter this vector. */
	public final Vec2 mul (float a) {
		return new Vec2(x * a, y * a);
	}

	/** Return the negation of this vector; does not alter this vector. */
	public final Vec2 negate () {
		return new Vec2(-x, -y);
	}

	/** Flip the vector and return it - alters this vector. */
	public final Vec2 negateLocal () {
		x = -x;
		y = -y;
		return this;
	}

	/** Add another vector to this one and returns result - alters this vector. */
	public final Vec2 addLocal (Vec2 v) {
		x += v.x;
		y += v.y;
		return this;
	}

	/** Adds values to this vector and returns result - alters this vector. */
	public final Vec2 addLocal (float x, float y) {
		this.x += x;
		this.y += y;
		return this;
	}

	/** Subtract another vector from this one and return result - alters this vector. */
	public final Vec2 subLocal (Vec2 v) {
		x -= v.x;
		y -= v.y;
		return this;
	}

	/** Multiply this vector by a number and return result - alters this vector. */
	public final Vec2 mulLocal (float a) {
		x *= a;
		y *= a;
		return this;
	}

	/** Get the skew vector such that dot(skew_vec, other) == cross(vec, other) */
	public final Vec2 skew () {
		return new Vec2(-y, x);
	}

	/** Get the skew vector such that dot(skew_vec, other) == cross(vec, other) */
	public final void skew (Vec2 out) {
		out.x = -y;
		out.y = x;
	}

	/** Return the length of this vector. */
	public final float length () {
		return MathUtils.sqrt(x * x + y * y);
	}

	/** Return the squared length of this vector. */
	public final float lengthSquared () {
		return (x * x + y * y);
	}

	/** Normalize this vector and return the length before normalization. Alters this vector. */
	public final float normalize () {
		float length = length();
		if (length < Settings.EPSILON) {
			return 0f;
		}

		float invLength = 1.0f / length;
		x *= invLength;
		y *= invLength;
		return length;
	}

	/** True if the vector represents a pair of valid, non-infinite floating point numbers. */
	public final boolean isValid () {
		return !Float.isNaN(x) && !Float.isInfinite(x) && !Float.isNaN(y) && !Float.isInfinite(y);
	}

	/** Return a new vector that has positive components. */
	public final Vec2 abs () {
		return new Vec2(MathUtils.abs(x), MathUtils.abs(y));
	}

	public final void absLocal () {
		x = MathUtils.abs(x);
		y = MathUtils.abs(y);
	}

	// @Override // annotation omitted for GWT-compatibility
	/** Return a copy of this vector. */
	public final Vec2 clone () {
		return new Vec2(x, y);
	}

	@Override
	public final String toString () {
		return "(" + x + "," + y + ")";
	}

	/*
	 * Static
	 */

	public final static Vec2 abs (Vec2 a) {
		return new Vec2(MathUtils.abs(a.x), MathUtils.abs(a.y));
	}

	public final static void absToOut (Vec2 a, Vec2 out) {
		out.x = MathUtils.abs(a.x);
		out.y = MathUtils.abs(a.y);
	}

	public final static float dot (final Vec2 a, final Vec2 b) {
		return a.x * b.x + a.y * b.y;
	}

	public final static float cross (final Vec2 a, final Vec2 b) {
		return a.x * b.y - a.y * b.x;
	}

	public final static Vec2 cross (Vec2 a, float s) {
		return new Vec2(s * a.y, -s * a.x);
	}

	public final static void crossToOut (Vec2 a, float s, Vec2 out) {
		final float tempy = -s * a.x;
		out.x = s * a.y;
		out.y = tempy;
	}

	public final static void crossToOutUnsafe (Vec2 a, float s, Vec2 out) {
		assert (out != a);
		out.x = s * a.y;
		out.y = -s * a.x;
	}

	public final static Vec2 cross (float s, Vec2 a) {
		return new Vec2(-s * a.y, s * a.x);
	}

	public final static void crossToOut (float s, Vec2 a, Vec2 out) {
		final float tempY = s * a.x;
		out.x = -s * a.y;
		out.y = tempY;
	}

	public final static void crossToOutUnsafe (float s, Vec2 a, Vec2 out) {
		assert (out != a);
		out.x = -s * a.y;
		out.y = s * a.x;
	}

	public final static void negateToOut (Vec2 a, Vec2 out) {
		out.x = -a.x;
		out.y = -a.y;
	}

	public final static Vec2 min (Vec2 a, Vec2 b) {
		return new Vec2(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y);
	}

	public final static Vec2 max (Vec2 a, Vec2 b) {
		return new Vec2(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y);
	}

	public final static void minToOut (Vec2 a, Vec2 b, Vec2 out) {
		out.x = a.x < b.x ? a.x : b.x;
		out.y = a.y < b.y ? a.y : b.y;
	}

	public final static void maxToOut (Vec2 a, Vec2 b, Vec2 out) {
		out.x = a.x > b.x ? a.x : b.x;
		out.y = a.y > b.y ? a.y : b.y;
	}

	/** @see java.lang.Object#hashCode() */
	@Override
	public int hashCode () { // automatically generated by Eclipse
		final int prime = 31;
		int result = 1;
		result = prime * result + Float.floatToIntBits(x);
		result = prime * result + Float.floatToIntBits(y);
		return result;
	}

	/** @see java.lang.Object#equals(java.lang.Object) */
	@Override
	public boolean equals (Object obj) { // automatically generated by Eclipse
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		Vec2 other = (Vec2)obj;
		if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) return false;
		if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) return false;
		return true;
	}
}
